#include "ConstruitClasse.h"

#include "Classe.h"

#include "Key.h"

#include "Qlib.h"

#include "AugPourcent.h"
#include "SelectionComp.h"
#include "SelectionSort.h"

#include <QtGui/QVBoxLayout>
#include <QtGui/QHBoxLayout>
#include <QtGui/QGroupBox>
#include <QtGui/QPushButton>
#include <QtGui/QLCDNumber>
#include <QtGui/QCheckBox>
#include <QtGui/QPushButton>
#include <QtGui/QButtonGroup>
#include <QtGui/QGridLayout>
#include <QtGui/QSpinBox>
#include <QtGui/QComboBox>
#include <QtCore/QStringList>
#include <QtGui/QLabel>

#include <QtGui/QApplication>

#include <algorithm>

using namespace std;

DebugLevelType ConstruitClasse::gestionListe = BLANK;

/**
 * Constructeur par défaut. Récupère l'ensembles des compétences de
 * classe et éventuels ensembles de compétences, et permet de
 * construire la classe.
 *
 * @param[in] classe pointeur sur la Classe
 * @param[in] race nom de la Race du futur PJ.
 * @param[in] valTraits tableau contenant les traits.
 * @param[in] pp nombre de pp restant de l'étape précédente
 * @param[in] parent pointeur sur le widget parent (orphelin par défaut)
 */
ConstruitClasse::ConstruitClasse(Classe* classe, const string& race,
				 const vector< unsigned > & valTraits,
				 const int& pp, QWidget* parent)
  : BaseUI( "ConstruitClasse", parent ), PP ( pp ), derniereComp( 0 ),
    annuleEnsemble( false ), mRace( race )
{
  ValeursTraits.assign( valTraits.begin(), valTraits.end() );

  mClasse = classe;

  boite = new QGroupBox( trUtf8("Choix des compétences :"), this);
  boiteRes = 0;
  boiteXp = new QGroupBox( trUtf8("Points d'expériences"), this );
  QVBoxLayout *xpLayout = new QVBoxLayout( boiteXp );

  nbrPP = new QLCDNumber(this);
  nbrPP -> setSegmentStyle(QLCDNumber::Flat);

  nbrXP = new QSpinBox( this );
  nbrXP -> setMinimum( 1 );
  nbrXP -> setMaximum( 10000000 );
  nbrXP -> setToolTip( trUtf8("Si le personnage ne part pas du niveau 1\n mettre ici le nombre de XP de cette classe.") );

  mClasse -> donneesEnsCompetences(nbrEns, nbrCompEns);

  xpLayout -> addWidget( nbrXP );

  boiteXp -> setLayout( xpLayout );

  preparerAffichage();

  if (nbrEns > 0)
    prepareEnsCompetences();
  else
    boiteEns = 0;

  boutons.push_back( new QPushButton( trUtf8("&Quitter"), this ) );
  boutons.back() -> setToolTip( trUtf8("Quitter le programme") );
  connect( boutons.back()  , SIGNAL( clicked() ), this, SLOT( annuler() ) );
  boutons.push_back( new QPushButton( trUtf8("&Update"), this ) );
  boutons.back() -> setToolTip( trUtf8("Mettre à jour l'affichage") );
  connect( boutons.back() , SIGNAL( clicked() ), this, SLOT( miseAJour() ) );
  boutons.push_back( new QPushButton( trUtf8("&OK"), this ) );
  boutons.back() -> setToolTip( trUtf8("Accepter et continuer") );
  connect( boutons.back(), SIGNAL( clicked() ), this, SLOT( fermer() ) );

  fenetreLayout -> addWidget( nbrPP );
  if (boiteRes != 0)
    fenetreLayout -> addWidget( boiteRes );
  fenetreLayout -> addWidget( boite );
  if (boiteEns != 0)
    fenetreLayout -> addWidget( boiteEns );
  fenetreLayout -> addWidget( boiteXp );

  setFenetreLayout();
}


/**
 * Destructeur, ne fait rien de spécial
 */
ConstruitClasse::~ConstruitClasse()
{}

/**
 * Effectue le choix des compétences de classe. Réalise l'affichage du
 * widget et vérifie ensuite si la Classe possède des compétences
 * liées à un pourcentage.
 *
 * @return StatusCode \e true si choix validé, \e false sinon, le
 * nombre de PP restant est donné par valeur().
 */
StatusCode ConstruitClasse::classeChoisie()
{
  /*Debug::*/empileNiveau( &interne );
  func() << "ConstruitClasse::classeChoisie -> début" << endmsg;

  this -> show();

  qApp -> exec();

  if ( mClasse -> possedePourcent() )
    {
      AugPourcent gestionPourcent( mClasse, false, this );
      gestionPourcent . exec();
    }

  func() << "ConstruitClasse::classeChoisie -> fin" << endmsg;
  /*Debug::*/depileNiveau();

  return StatusCode( OK, PP );
}

/**
 * Permet de modifier la valeur de ConstruitClasse::gestionListe.
 *
 * @param[in] level nouvelle valeur de ConstruitClasse::gestionListe.
 */
void ConstruitClasse::setLevel( const DebugLevelType& level, const unsigned& )
{
  gestionListe = level;
}

/**
 * Construit le contenu de la fenêtre. Prépare un groupe de
 * restrictions, un groupe de compétences et le cas échéant, un menu
 * déroulant avec les ensembles de compétences.
 *
 * Le niveau de sortie est paramétré par BaseUI::interne :\n
 * - \e INFO affiche le nombre de compétences et de restrictions.\n
 * - \e FUNC affiche les messages de début et de fin de méthode.\n
 * - \e LINFO affiche les compétences pour lesquels un bouton trinaire
 * est nécessaire.\n
 * - \e DEBUG affiche le numéro du bouton et le nom de ce dernier.
 */
void ConstruitClasse::preparerAffichage()
{
  /*Debug::*/empileNiveau( &interne );
  func() << "ConstruitClasse::preparerAffichage -> début" << endmsg;

  PP += mClasse -> pp();

  nbrPP -> display(PP);

  mClasse -> copieCompetencesClasse( nbrComp, Competences,
				     nbrRes, Restrictions );

  info() << nbrComp << " compétences et " << nbrRes << " restrictions"
	 << endmsg;

  aCompRes.assign(nbrComp + nbrRes, false);

  etatsTriples.assign(nbrComp + nbrRes, false);

  analyseListe();

  QGridLayout *boiteLayout = new QGridLayout(boite);

  groupe = new QButtonGroup(this);

  groupe -> setExclusive ( false );

  map<Key, unsigned int>::const_iterator it, Begin(mapEtatsTriples.begin()),
    End(mapEtatsTriples.end());

  unsigned int pos, num;

  string tmpStr;

  for (it = Begin; it != End; it++)
    {
      num = it -> second;
      pos = it -> first.Nombre;
      if (num == nbrComp) break;
      QCheckBox *tmpBouton;
      if ( it -> first.Etat == 1) // Il nous faut un tristate!
	{
	  tmpStr = Competences[num].nom();
	  tmpStr = tmpStr.substr(0, tmpStr.find("(") - 1 );
	  tmpBouton = new QCheckBox( trUtf8( tmpStr.c_str() ), this );
	  tmpBouton -> setTristate( true );
	  groupe -> addButton(tmpBouton, pos);
	  it++;
	  linfo() << "Tristate pour " << tmpStr << endmsg;
	}
      else
	groupe -> addButton(new QCheckBox( trUtf8( Competences[num].nom().c_str() ), this ), pos);

      debug() << pos << " -> "
	      << toUtf8StdString( groupe -> button( pos ) -> text() ) << endmsg;
      boiteLayout -> addWidget(groupe -> button(pos), pos % 15, pos / 15 );
    }

  boite -> setLayout(boiteLayout);

  if (nbrRes != 0)
    {
      boiteRes = new QGroupBox( trUtf8("Choix des restrictions :"), this);
      QGridLayout *boiteResLayout = new QGridLayout(boiteRes);
      for (unsigned int i(0); it != End; it++, i++)
	{
	  num = it -> second;
	  pos = it -> first.Nombre;
	  
	  groupe -> addButton(new QCheckBox( trUtf8( (Restrictions[i].nom()).c_str() ), this), pos);
	  boiteResLayout -> addWidget(groupe -> button(pos), i % 5, i / 5 );
	}
      boiteRes -> setLayout(boiteResLayout);
    }

  connect(groupe, SIGNAL(buttonClicked(int)), this, SLOT(ajouter(int)));

  func() << "ConstruitClasse::preparerAffichage -> fin" << endmsg;
  /*Debug::*/depileNiveau();
}

/**
 * Parcourt la liste des compétences et des restrictions pour
 * identifier lesquelles ont besoin d'un boutons trinaire. En fait
 * uniquement les compétences peuvent avoir de tels boutons, mais les
 * restrictions sont également traitées ici, comme ça le conteneur
 * etatsTriples et le tableau associaif mapEtatsTriples sont
 * correctement remplis.
 *
 * Dans un premier temps on passe en revue toutes les compétences,
 * pour détecter lesquelles auront besoin d'un bouton
 * trinaire. Ensuite on itère sur les restrictions, mais cette fois
 * sans s'inquiéter.
 *
 * @see BaseListeSel
 *
 * Le niveau de sortie est paramétré par ConstruitClasse::gestionListe
 * :\n
 * - \e INFO affiche la progression du processus ("Compétences OK" et
 * "Restrictions OK").\n
 * - \e FUNC affiche les messages de début et de fin de méthode.\n
 * - \e LINFO affiche pour chaque élément le numéro du bouton
 * correspondant, la clé, la compétence et le coût.
 */
void ConstruitClasse::analyseListe()
{
  /*Debug::*/empileNiveau( &gestionListe );
  func() << "ConstruitClasse::analyseListe -> début" << endmsg;

  unsigned int i, j, compteur(0);

  size_t loc;

  for (i = 0; i < nbrComp; i++)
    {
      if (etatsTriples[i]) continue;
      loc = (Competences[i].nom()).find("("); 
      if (loc == string::npos)
	{
	  Key clef(compteur, 0);
	  mapEtatsTriples[clef] = i;
	  linfo() << "élément " << i << " =>  bouton " << compteur
		  << " clé " << clef.Nombre << " " << clef.Etat
		  << " pour " << Competences[i].nom() << " cout "
		  << Competences[i].prix() << endmsg;
	  compteur++;
	  continue;
	}
      for (j = i + 1; j < nbrComp; j++)
	{
	  if ( (Competences[j].nom()).find("(") == loc)
	    {
	      if ( (Competences[i].nom()).substr(0, loc) == 
		   (Competences[j].nom()).substr(0, loc)
		   )
		{
		  etatsTriples[i] = true;
		  etatsTriples[j] = true;
		  if ( Competences[i].prix() < Competences[j].prix() )
		    {
		      Key clef1(compteur, 1);
		      Key clef2(compteur, 2);
		      compteur++;
		      mapEtatsTriples[clef1] = i;
		      mapEtatsTriples[clef2] = j;
		      linfo() << "élément " << i << " =>  bouton " << compteur
			      << " clé " << clef1.Nombre << " " << clef1.Etat
			      << ",  " << clef2.Nombre << " " << clef2.Etat
			      << " pour " << Competences[i].nom()
			      << ", " << Competences[j].nom()
			      << " cout " << Competences[i].prix() << ", "
			      << Competences[j].prix() << endmsg;
		      break;
		    }
		  else
		    {
		      Key clef1(compteur, 1);
		      Key clef2(compteur, 2);
		      compteur++;
		      mapEtatsTriples[clef1] = j;
		      mapEtatsTriples[clef2] = i;
		      linfo() << "élément " << i << " =>  bouton " << compteur
			      << " clé " << clef1.Nombre << " " << clef1.Etat
			      << ",  " << clef2.Nombre << " " << clef2.Etat
			      << " pour " << Competences[i].nom()
			      << ", " << Competences[j].nom()
			      << " cout " << Competences[i].prix() << ", "
			      << Competences[j].prix() << endmsg;
		      break;
		    }
		}
	    }
	  else if (j == nbrComp - 1)
	    {
	      Key clef(compteur, 0);
	      mapEtatsTriples[clef] = i;
	      linfo() << "élément " << i << " =>  bouton " << compteur
		      << " clé " << clef.Nombre << " " << clef.Etat
		      << " pour " << Competences[i].nom() << " cout "
		      << Competences[i].prix() << endmsg;
	      compteur++;
	    }
	}
    }
  
  derniereComp = compteur;

  info() << "Compétences OK" << endmsg;

  for (i = 0; i < nbrRes; i++)
    {
      Key clef(compteur, 0);
      mapEtatsTriples[clef] = i + nbrComp;
      debug() << "élément " << i + nbrComp << " =>  bouton " << compteur
	      << " clé " << clef.Nombre << " " << clef.Etat
	      << " pour " << Restrictions[i].nom() << " donnant "
	      << Restrictions[i].prix() << endmsg;
      compteur++;
    }

  info() << "Restrictions OK" << endmsg;


  func() << "ConstruitClasse::analyseListe -> fin" << endmsg;
  /*Debug::*/depileNiveau();
}

/**
 * Permet d'ajouter proprement une compétence. En effet comme on a des
 * boutons trinaires, il fait gérer ça prudemment, notamment pour le
 * coût.
 *
 * On regarde en premier lieu si le bouton est trinaire. Si oui, on
 * récupère son état et on construit les deux clés possibles pour ce
 * bouton. L'état actuel permet de savoir si on passe de rien à la
 * compétence "bon marché", de "bon marché" à "cher" ou encore si on
 * supprime cette compétence. Les deux clés créées permettent de
 * calculer le nombre de PP à ajouter ou à soustraire au total. Le nom
 * affiché est également changé.
 *
 * Ensuite on vérifie que le coût est inférieur au nombre actuel de PP
 * restant, ceci également pour un bouton conventionnel.
 *
 * Dans tous les cas l'affichage du nombre de PP restant est mis à
 * jour.
 *
 * Le niveau de sortie est paramétré par BaseUI::interne :\n
 * - \e FUNC affiche les messages de début et de fin de méthode.\n
 * - \e LFUNC affiche les changements d'une compétence liée à un bouton
 * trinaire et la sélection / supression d'une compétence.\n
 * - \e DEBUG affiche l'état d'un bouton trinaire.
 *
 * @param[in] comp numéro du bouton activé.
 */
void ConstruitClasse::ajouterCompetence( const int& comp )
{
  /*Debug::*/empileNiveau( &interne );
  func() << "ConstruitClasse::ajouterCompetence -> début" << endmsg;

  int cout(0);
  unsigned int pos(0);
  if ( static_cast<QCheckBox*>(groupe -> button(comp)) -> isTristate() )
    {
      unsigned int etat(static_cast<QCheckBox*>(groupe -> button(comp)) -> checkState());
      debug() << "Etat " << etat << endmsg;
      Key clef1(static_cast<unsigned int>(comp), 1);
      Key clef2(static_cast<unsigned int>(comp), 2);
      string tmpStr((Competences[mapEtatsTriples[clef1]]).nom());
      unsigned int pos(999);
      switch(etat)
	{
	case 0:
	  // On passe de très cher à rien
	  cout = -Competences[mapEtatsTriples[clef2]].prix();
	  aCompRes[mapEtatsTriples[clef2]] = false;
	  tmpStr = tmpStr.substr(0, tmpStr.find("(") - 1 );
	  groupe -> button(comp) -> setText( QString::fromUtf8( tmpStr.c_str() ) );
	  lfunc() << "On supprime " << tmpStr << endmsg;
	  break;
	case 1:
	  pos = mapEtatsTriples[clef1];
	  cout = Competences[ pos ].prix();
	  aCompRes[ pos ] = true;
	  groupe -> button(comp) -> setText( QString::fromUtf8( (Competences[ pos ].nom()).c_str()) );
	  lfunc() << "On passe à " << Competences[ pos ].nom()
		  << endmsg;
	  break;
	case 2:
	  pos = mapEtatsTriples[clef2];
	  cout = Competences[ pos ].prix() - Competences[mapEtatsTriples[clef1]].prix();
	  groupe -> button(comp) -> setText( QString::fromUtf8( (Competences[ pos ].nom()).c_str() ) );
	  aCompRes[mapEtatsTriples[clef1]] = false;
	  aCompRes[ pos ] = true;
	  lfunc() << "On passe à " << Competences[ pos ].nom()
		  << endmsg;
	  break;
	default:
	  warning() << "ne sais pas quoi faire avec " << etat << endmsg;
	}
      PP -= cout;
      if ( pos != 999 && Competences[ pos ] . sousCategorieDefinie() )
	/*
	 * Il s'agit d'un ensemble de compétence annexes, comme
	 * les talents de Voleurs pour les Mages.
	 */
	{
	  debug() << "Attention, il y a des sous-catégories" << endmsg;
	  const vector< CompetenceClasse > *ptr = mClasse ->
	    listesCompetences( Competences[ pos ].sousCategorie() );
	  
	  vector< CompetenceClasse >::const_iterator it, Begin( ptr -> begin() ),
	    End( ptr -> end() );
	  
	  // On vérifie si tout les coûts sont nuls.
	  
	  bool toutGratuit( true );
	  
	  vector< string > noms;
	  vector< unsigned int> couts;
	  
	  for (it = Begin; it != End; it++)
	    {
	      noms.push_back( it -> nom() );
	      couts.push_back( it -> prix() );
	      toutGratuit &= ( it -> prix() == 0);
	    }
	  
	  StatusCode sc;
	  
	  vector< unsigned int > choix;
	  
	  if ( toutGratuit  )
	    /*
	     * Le choix sera gratuit.
	     *
	     * => on a besoin ici de SelectionSort.
	     */
	    {
	      debug() << "Le choix est unique" << endmsg;
	      SelectionSort catUnique( ptr -> size(), noms,
					     "Sélection de la compétence",
					     this );

	      catUnique . setExclusif( Competences[ pos ].choixExclusif() );
	      
	      sc = catUnique.selectionFaite( choix );
	      
	      if ( sc )
		{
		  linfo() << "Sélection de la compétence "
			  << ptr -> at( sc.valeur() ) . nom() << endmsg;
		  mapTiroirs[ pos ] . assign( choix.begin(), choix.end() );
		}
	    }
	  else
	    /*
	     *
	     * => on a besoin ici de SelectionComp.
	     */
	    {
	      debug() << "Le choix est multiple" << endmsg;
	      SelectionComp catsMultiples( ptr -> size(),
					   noms, "Sélection des compétences",
					   couts, PP, this );

	       catsMultiples . setExclusif( Competences[ pos ].choixExclusif() );

		  sc = catsMultiples.selectionFaite( choix );

		  if ( sc )
		    {
		      // Il faut parcourir le conteneur choix.
		      vector< unsigned int >::const_iterator it,
			End( choix.end());

		      mapTiroirs[ pos ] . assign( choix.begin(), choix.end() );

		      for (it = choix.begin(); it != End; it++)
			linfo() << "Sélection de la compétence "
				<< ptr -> at( *it ) .nom() << endmsg;
		    }
		  PP = sc.valeur();
	    }
	}
    }
  else
    {
      Key clef(comp);
      pos = mapEtatsTriples[clef];
      cout = Competences[mapEtatsTriples[clef]].prix();
      // debug() << "élément " << pos << " =>  bouton " << comp
      // 	      << " clé " << clef.Nombre << " " << clef.Etat
      // 	      << " pour " << Competences[mapEtatsTriples[clef]].nom()
      // 	      << " cout " << Competences[mapEtatsTriples[clef]].prix() << endmsg;
      if (groupe -> button(comp) -> isChecked())
	{
	  PP -= cout;
	  aCompRes[pos] = true;
	  lfunc() << "Sélection de " << Competences[mapEtatsTriples[clef]].nom()
		  << endmsg;
	  if ( Competences[ pos ] . sousCategorieDefinie() )
	    /*
	     * Il s'agit d'un ensemble de compétence annexes, comme
	     * les talents de Voleurs pour les Mages.
	     */
	    {
	      debug() << "Attention, il y a des sous-catégories" << endmsg;
	      const vector< CompetenceClasse > *ptr = mClasse -> listesCompetences( Competences[ pos ].sousCategorie() );

	      vector< CompetenceClasse >::const_iterator it, Begin( ptr -> begin() ),
		End( ptr -> end() );

	      // On vérifie si tout les coûts sont nuls.

	      bool toutGratuit( true );

	      vector< string > noms;
	      vector< unsigned int> couts;

	      for ( it = Begin; it != End; it++)
		{
		  noms.push_back( it -> nom() );
		  couts.push_back( it -> prix() );
		  toutGratuit &= ( it -> prix() == 0);
		}

	      StatusCode sc;

	      vector< unsigned int > choix;

	      if ( toutGratuit  )
		/*
		 * Le choix sera unique, comme les écoles de magie en
		 * Prêtre ou les sphères de pouvoir en Mage.
		 *
		 * => on a besoin ici de SelectionSort.
		 */
		{
		  SelectionSort categorieUnique( ptr -> size(), noms,
						 "Sélection de la compétence",
						 this);

		  categorieUnique . setExclusif( true );

		  sc = categorieUnique.selectionFaite( choix );

		  if ( sc )
		    {
		      linfo() << "Sélection de la compétence "
			      << ptr -> at( sc.valeur() ) . nom() << endmsg;
		    }
		}
	      else
		/*
		 * On va pouvoir sélectionner de multiples items,
		 * comme pour les talents de Voleur pour un Mage.
		 *
		 * => on a besoin ici de SelectionComp.
		 */
		{
		  SelectionComp categoriesMultiples( ptr -> size(),
						     noms, "Sélection des compétences",
						     couts,
						     PP, this );

		  sc = categoriesMultiples.selectionFaite( choix );

		  if ( sc )
		    {
		      // Il faut parcourir le conteneur choix.
		      vector< unsigned int >::const_iterator it,
			End( choix.end());

		      for (it = choix.begin(); it != End; it++)
			linfo() << "Sélection de la compétence "
				<< ptr -> at( *it ) .nom() << endmsg;
		    }
		  PP = sc.valeur();
		}
	    }
	}
      else
	{
	  PP += cout;

	  lfunc() << "Suppression de "
		  << Competences[mapEtatsTriples[clef]].nom() << endmsg;
	  aCompRes[pos] = false;
	}
    }

  nbrPP -> display(PP);

  func() << "ConstruitClasse::ajouterCompetence -> fin" << endmsg;
  /*Debug::*/depileNiveau();
}

/**
 * Permet d'ajouter une restriction. Aucune restriction n'est
 * implémentée par un bouton trinaire, alors c'est plus simple que
 * pour les compétences.
 *
 * Ici on fait attention lors de la suppression d'une restriction que
 * le total de PP ne devienne pas négatif.
 *
 * Le nombre de PP restant est mis à jour sur l'affichage.
 *
 * Le niveau de sortie est paramétré par BaseUI::interne :\n
 * - \e FUNC affiche les messages de début et de fin de méthode.\n
 * - \e DEBUG affiche le numéro du bouton, le numéro de la restriction,
 * la clé le nom et le coût de la restriction.
 *
 * @param[in] res numéro du bouton activé.
 */
void ConstruitClasse::ajouterRestriction( const int& res )
{
  /*Debug::*/empileNiveau( &interne );
  func() << "ConstruitClasse::ajouterRestriction .> début" << endmsg;

  int cout;
  unsigned int pos;

  Key clef(res);
  pos = mapEtatsTriples[clef];
  cout = Restrictions[pos - nbrComp].prix();
  debug() << "élément " << pos << " =>  bouton " << res
	  << " clé " << clef.Nombre << " " << clef.Etat
	  << " pour " << Restrictions[pos - nbrComp].nom()
	  << " cout " << Restrictions[pos - nbrComp].prix() << endmsg;
  if (groupe -> button(res) -> isChecked())
    {
      PP += cout;
      aCompRes[pos] = true;
    }
  else
    {
      if ( PP >= cout )
	{
	  PP -= cout;
	  aCompRes[pos] = false;
	}
      else
	warning() << "Pas assez de PP!" << endmsg;
    }

  nbrPP -> display(PP);

  func() << "ConstruitClasse::ajouterRestriction -> fin" << endmsg;
  /*Debug::*/depileNiveau();
}

/**
 * Si la Classe possède des ensembles de compétences, alors c'est ici
 * que l'on va créer le menu déroulant qui contient les noms des
 * ensembles.
 *
 * On vérifie si tous les ensembles sont gratuits, si c'est le cas, on
 * est obligé d'en choisir un, mais dans le cas contraire, il faut
 * pouvoir en sélectionner aucun, et on ajoute "Aucun" au menu
 * déroulant.
 *
 * Le niveau de sortie est paramétré par BaseUI::interne :\n
 * - \e FUNC affiche les messages de début et de fin de méthode.\n
 */
void ConstruitClasse::prepareEnsCompetences()
{
  /*Debug::*/empileNiveau( &interne );
  func() << "ConstruitClasse::prepareEnsCompetences -> début" << endmsg;

  listeEns = new QComboBox( this );

  boiteEns  = new QGroupBox( trUtf8("Ensembles de compétences"), this );

  QHBoxLayout *ensLayout = new QHBoxLayout( boiteEns );
  QLabel *legende = new QLabel( trUtf8("Ensemble sélectionné :"), boiteEns );

  ensLayout -> addWidget( legende );

  Ensembles.assign( mClasse -> ensembles() -> begin(),
		    mClasse -> ensembles() -> end() );

  QStringList tmp;

  for (unsigned int i(0); i < nbrEns; i++)
    {
      tmp . append( QString::fromUtf8( Ensembles[i] . nom() . c_str() ) );
      annuleEnsemble |= (Ensembles[i] . prix() != 0  );
    }

  if ( annuleEnsemble )
    {
      // On doit pouvoir annuler un tel choix, car non gratuit
      tmp . append( QString::fromUtf8( "Aucun" ) );
    }
  else
    {
      boite -> setEnabled( false );
      if ( boiteRes != 0 )
	boiteRes -> setEnabled( false );
    }

  listeEns -> addItems( tmp );

  ensLayout -> addWidget( listeEns );

  boiteEns -> setLayout( ensLayout );

  func() << "ConstruitClasse::prepareEnsCompetences -> fin" << endmsg;
  /*Debug::*/depileNiveau();
}

/**
 * Cette méthode est connectée au "groupe logique" des boutons, et
 * permet de faire le tri entre compétences et restrictions.
 *
 * @param[in] bouton numéro du bouton activé.
 */
void ConstruitClasse::ajouter( const int& bouton )
{
  if (bouton < derniereComp)
    ajouterCompetence(bouton);
  else
    ajouterRestriction(bouton);
}

/**
 * On redéfinit la méthode fermer() pour modifier la classe lorsque le
 * choix est validé et que l'on ferme la fenêtre.
 *
 * On écrit les compétences / restrictions dans la Classe, ainsi que
 * le nombre de points d'expérience.
 *
 * Pour les compétences à tiroir, on ajoute la valeur d'AdresseBase et
 * on copie le tout dans les compétences de Classe. On fait appel ici
 * à std::transform, std::bind1st et std::plus pour gagner du temps.
 *
 * Le niveau de sortie est paramétré par BaseUI::interne :\n
 * - \e FUNC affiche les messages de début et de fin de méthode.\n
 * - \e DEBUG affiche le nombre total de compétences + restrictions,
 * ainsi que chacune d'entre elle (son numéro uniquement).
 */
void ConstruitClasse::fermer()
{
  /*Debug::*/empileNiveau( &interne );
  func() << "ConstruitClasse::fermer -> début" << endmsg;

  if ( PP < 0 )
    {
      func() << "ConstruitClasse::fermer -> fin" << endmsg;
      /*Debug::*/depileNiveau();
      return;
    }

  OK = true;

  vector<unsigned int> vec, tiroir;

  debug() <<  nbrComp + nbrRes << " trucs, taille = " << aCompRes.size()
	  << endmsg;

  for (unsigned int i(0); i < nbrComp + nbrRes; i++)
    {
      if (aCompRes[i])
	{
	  debug() << i << ' ';
	  vec.push_back(i);
	  if (i < nbrComp)
	    {
	      if ( Competences[i].pourcentage() )
		vec.push_back( Competences[i].valeurBase() );
	      else if ( Competences[i].sousCategorieDefinie() )
		{
		  tiroir = mapTiroirs[ i ];
		  std::transform( tiroir.begin(), tiroir.end(),
				  tiroir.begin(),
				  bind1st( plus<unsigned int>(),
					   Competences[i].adresseBase()) );
		  
		  vec.insert( vec.end(), tiroir.begin(), tiroir.end() );
		}
	    }
	}
    }
  debug() << endmsg;

  mClasse -> setCompetences( vec );

  mClasse -> setXP( nbrXP -> value() );

  func() << "ConstruitClasse::fermer -> fin" << endmsg;
  /*Debug::*/depileNiveau();

  close();
}

/**
 * Permet d'appliquer un ensemble de compétences. Dans un premier
 * temps on vérifie qu'il existe des ensembles de compétences. Ensuite
 * on supprime toutes les compétences individuelles déjà sélectionées,
 * puis on vérifie que l'on a assez de PP pour acheter l'ensemble. Si
 * oui, les compétences sont sélectionnées.
 *
 * Les spécialistes ont un fonctionnement un peu différent : chaque
 * école de magie non disponible est automatiquement désactivée, ce
 * qui veut dire qu'elle ne pourra en aucun cas être sélectionnée. De
 * plus, il faut enlever la dernière restriction (école supplémentaire
 * en opposition). Pour cela j'ai modifié le nombre de restrictions
 * dans la classe Specialiste dans le cas où on veut construire un
 * Spécialiste en mode graphique.
 *
 * Le niveau de sortie est paramétré par BaseUI::interne :\n
 * - \e INFO affiche les différentes étapes.\n
 * - \e FUNC affiche les messages de début et de fin de méthode.\n
 * - \e DEBUG affiche les différentes compétences de l'ensemble.\n
 */
void ConstruitClasse::miseAJour()
{
  /*Debug::*/empileNiveau( &interne );
  func() << "ConstruitClasse::miseAJour -> début" << endmsg;

  if (boiteEns == 0)
    {
      func() << "ConstruitClasse::miseAJour -> fin" << endmsg;
      /*Debug::*/depileNiveau();
      return;
    }

  if ( ! boite -> isEnabled() )
    boite -> setEnabled( true );
  if ( boiteRes != 0 && ! boiteRes -> isEnabled() )
    boiteRes -> setEnabled( true );

  const vector<unsigned int> *capas;
  unsigned int ensChoisi, i;

  ensChoisi = listeEns -> currentIndex();

  StatusCode sc( mClasse -> prerequisOK( mRace, ValeursTraits, ensChoisi ) );

  if ( !sc )
    {
      error() << sc.message() << endmsg;
      func() << "ConstruitClasse::miseAJour -> fin" << endmsg;
      /*Debug::*/depileNiveau();
      return;      
    }

  /*
   * On s'assure que tout est bien désactivé (et que l'on récupère les
   * PPs déjà dépensés).
   */
  for (i = 0; i < derniereComp + nbrRes; i++)
    {
      if ( groupe -> button( i ) -> isChecked() )
	{
	  groupe -> button( i ) -> setChecked( false );
	}
      if ( ! groupe -> button( i ) -> isEnabled() )
	groupe -> button( i ) -> setEnabled( true );	
    }

  info() << "Compétences nettoyées" << endmsg;

  if ( ensChoisi == nbrEns && annuleEnsemble )
    {
      func() << "ConstruitClasse::miseAJour -> fin" << endmsg;
      /*Debug::*/depileNiveau();
      return;
    }

  if ( PP < static_cast<int>( Ensembles[ ensChoisi ] . prix() ) )
    {
      warning() << "Pas assez de PP!" << endmsg;
      func() << "ConstruitClasse::miseAJour -> fin" << endmsg;
      /*Debug::*/depileNiveau();
      return;
    }

  capas = mClasse -> ensembles() -> at( ensChoisi ) . membres();

  // On applique le coût.
  PP -= Ensembles[ ensChoisi ] . prix();

  aCompRes.assign(nbrComp + nbrRes, false);

  info() << "Coût appliqué, compétences et restrictions réinitialisées." << endmsg;

  /*
   * On ajoute les compétences, mais sans passer par ajouter( ), sinon
   * le coût sera compté deux fois!
   */
  nbrCompEns = capas -> size();
  for (i = 0; i < nbrCompEns; i++)
    {
      debug() << capas -> at(i) - 1 << ' ';
      groupe -> button( capas -> at(i) - 1 ) -> setChecked( true );
      aCompRes[ capas -> at(i) - 1 ] = true;
    }
  debug() << endmsg;
  /*
   * On exclus les compétences incompatibles avec le choix de
   * l'ensemble.
   */
  capas =  mClasse -> ensembles() -> at( ensChoisi ) . exclus();
  nbrCompEns = capas -> size();
  for (i = 0; i < nbrCompEns; i++)
    {
      debug() << capas -> at(i) - 1 << ' ';
      groupe -> button( capas -> at(i) - 1 ) -> setEnabled( false );
    }
  debug() << endmsg;

  if ( mClasse -> classe() == "Spécialiste" )
    {
      for (i = 0; i < nbrEns; i++)
	{
	  if ( ! groupe -> button( i ) -> isChecked() )
	    groupe -> button( i ) -> setEnabled( false );
	  else
	    groupe -> button( i ) -> setEnabled( true );
	}
    }

  nbrPP -> display(PP);

  info() << "Affichage prêt" << endmsg;

  func() << "ConstruitClasse::miseAJour -> fin" << endmsg;
  /*Debug::*/depileNiveau();
}
